﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Avaxo.Scripting;
using Microsoft.Scripting;

namespace Avaxo.Console
{
    public class Editor : RichTextBox
    {
        private ILanguageEngine languageEngine;
        private Popup membersPopup;

        #region Properties

        public virtual ILanguageEngine LanguageEngine
        {
            get { return languageEngine; }
            set { languageEngine = value; }
        }

        public string Text
        {
            get
            {
                TextRange textRange = new TextRange( Document.ContentStart, Document.ContentEnd );
                return textRange.Text;
            }
        }

        public Popup MembersPopup
        {
            get
            {
                if ( membersPopup == null )
                {
                    membersPopup = new Popup();
                    membersPopup.Child = new ListBox();
                    membersPopup.PlacementTarget = this;
                    membersPopup.Placement = PlacementMode.Relative;
                    MembersPopup.PreviewKeyDown += OnMembersPopupPreviewKeyDown;
                }
                return membersPopup;
            }
        }

        protected ListBox MembersListBox
        {
            get { return MembersPopup.Child as ListBox; }
        }

        #endregion

        public Editor() : this( Scripting.LanguageEngine.DefaultEngine )
        {
        }

        public Editor( ILanguageEngine languageEngine )
        {
            LanguageEngine = languageEngine;
        }

        protected void HandleException( Exception ex )
        {
            DynamicStackFrame[] frames = RuntimeHelpers.GetDynamicStackFrames( ex );
            StringBuilder exception = new StringBuilder();
            if ( frames == null || frames.Length == 0 )
            {
                exception.Append( languageEngine.ScriptEngine.FormatException( ex ) );
                if ( exception.Length > 0 && !exception.ToString().EndsWith( Environment.NewLine ) )
                {
                    exception.Append( Environment.NewLine );
                }
            }
            else
            {
                exception.AppendFormat( "{0}: {1}{2}", ex.GetType(), ex.Message, Environment.NewLine );
                foreach ( DynamicStackFrame frame in frames )
                {
                    exception.AppendFormat( "  at {0} in {1}, line {2}{3}", frame.GetMethodName(), frame.GetFileName(),
                                            frame.GetFileLineNumber(), Environment.NewLine );
                }
            }

            WriteLine( exception.ToString(), TextType.Error );
        }

        protected void Write( string text, TextType type )
        {
            Run run = new Run( text );
            run.Foreground = new SolidColorBrush( TextColors.GetColor( type ) );
            if ( Document.Blocks.Count == 0 )
            {
                Document.Blocks.Add( new Paragraph() );
            }
            ( (Paragraph) Document.Blocks.LastBlock ).Inlines.Add( run );
            CaretPosition = run.ContentEnd;
        }

        protected void WriteLine( string text, TextType type )
        {
            Run run = new Run( text );
            run.Foreground = new SolidColorBrush( TextColors.GetColor( type ) );
            Document.Blocks.Add( new Paragraph( run ) );
        }

        protected override void OnPreviewKeyDown( KeyEventArgs e )
        {
            if ( e.Key == Key.OemPeriod )
            {
                e.Handled = true;
                HandleCodeSense();
                Write( ".", TextType.Default );
            }
            else if ( e.Key == Key.Escape )
            {
                if ( MembersPopup.IsOpen )
                {
                    membersPopup.IsOpen = false;
                    e.Handled = true;
                }
            }

            base.OnPreviewKeyDown( e );
        }

        private void OnMembersPopupPreviewKeyDown( object sender, KeyEventArgs e )
        {
            if ( e.Key == Key.Enter || e.Key == Key.Tab )
            {
                if ( MembersPopup.IsOpen )
                {
                    e.Handled = true;
                    HandleMemberSelect();
                }
            }
            else if ( e.Key == Key.Escape )
            {
                if ( MembersPopup.IsOpen )
                {
                    membersPopup.IsOpen = false;
                    e.Handled = true;
                }
            }
        }

        private void HandleMemberSelect()
        {
            MembersPopup.IsOpen = false;
            Focus();
            Write( MembersListBox.SelectedItem.ToString(), TextType.Input );
        }

        private void HandleCodeSense()
        {
            //TODO: check for TokenTriggers.MemberSelect
            TextRange range = new TextRange( CaretPosition.GetLineStartPosition( 0 ), CaretPosition );
            string enteredText = range.Text;
            int lastSpaceIndex = enteredText.LastIndexOf( " " );
            if ( lastSpaceIndex < 0 )
            {
                lastSpaceIndex = 0;
            }
            string expression = enteredText.Substring( lastSpaceIndex ).Trim();
            string name = FindExpression( expression );
            string[] members = languageEngine.CodeSenseProvider.GetMemberNames( name );
            Array.Sort( members );
            ShowMembersList( members );
        }

        private static string FindExpression( string expression )
        {
            //TODO: fix this
            return expression;
        }

        private static IEnumerable<string> FilterMembers( IEnumerable<string> members )
        {
            foreach ( string member in members )
            {
                if ( member.IndexOf( "_" ) < 0 )
                {
                    yield return member;
                }
            }
        }

        private void ShowMembersList( IEnumerable<string> members )
        {
            MembersListBox.Items.Clear();

            foreach ( string member in FilterMembers( members ) )
            {
                MembersListBox.Items.Add( member );
            }

            if ( MembersListBox.Items.Count > 0 )
            {
                MembersPopup.IsOpen = true;

                PositionMembersPopup();
                MembersListBox.SelectedIndex = 0;
                MembersListBox.ScrollIntoView( MembersListBox.SelectedValue );
                MembersListBox.Focus();
            }
        }

        private void PositionMembersPopup()
        {
            Rect rect = CaretPosition.GetCharacterRect( LogicalDirection.Forward );
            MembersPopup.HorizontalOffset = rect.Left;
            MembersPopup.VerticalOffset = Regex.Matches( Text, Environment.NewLine ).Count *
                                          ( Document.Blocks.FirstBlock.FontSize + 4 ) + 2;
        }
    }
}